#include "Eventloop.h"
#include "Channel.h"
#include "Poller.h"
#include "log/Logger.h"
#include "utility/assertion.h"

#include <poll.h>
#include <algorithm>

namespace _ {

static thread_local Eventloop *loopOfCurrentThread = nullptr;

const int kPollTime = 10000;
}


Eventloop *Eventloop::LOOP_OF_CURRENT_THREAD() {
    return _::loopOfCurrentThread;
}

Eventloop::Eventloop(/* args */):
    _looping{false},
    _tid{CurrentThread::tid()},
    _quit{false},
    _eventHandling{false},
    _poller{Poller::new_defatult_poller(this)},
    _currentActiveChannel{nullptr}
{
    LOG_VERBOSE << "Eventloop created" << this << "in thread" << _tid;
    if (_::loopOfCurrentThread != nullptr) {
        LOG_FATAL << "Another loop " << _::loopOfCurrentThread
                  << " existed in this thread " << _tid;
    }
    else {
        _::loopOfCurrentThread = this;
    }
}

Eventloop::~Eventloop() {
    _::loopOfCurrentThread = nullptr;
}

void Eventloop::loop() {
    assert(!_looping);
    ASSERT_IN_THIS_THREAD();
    LOG_VERBOSE << "Eventloop " << this << " start looping";

   // ::poll(NULL, 0, 5*1000);
    while (!_quit)
    {
        _activeChannels.clear();
        _pollReturnTime = _poller->poll(_::kPollTime, &_activeChannels);

        _eventHandling = true;
        for (auto &ch : _activeChannels) {
            _currentActiveChannel = ch;
            ch->handle_event(_pollReturnTime);
        }

        _currentActiveChannel = nullptr;
        _eventHandling = false;
        // dopendingfunctors
    }

    LOG_VERBOSE << "Eventloop " << this << "stop looping";
    _looping = false;
}

void Eventloop::assert_in_this_thread(const char *func, const char *file, int line) const {
    if (is_in_this_thread()) {
        return;
    }
    LOG_FATAL << "Eventloop " << this << " was created in " << _tid
              << ", current tid=" << CurrentThread::tid() << " in " << func
              << "() of " << file << ":" << line;
}

bool Eventloop::is_in_this_thread() const {
    return _tid == CurrentThread::tid();
}


void Eventloop::quit() {
    assert(!_quit);
    _quit = true;
    if (!is_in_this_thread()) {
        // wakeup();
    }
}

void Eventloop::update_channel(Channel *channel) {
    assert(channel->owner_loop() == this);
    ASSERT_IN_THIS_THREAD();
    _poller->update_channel(channel);
}


void Eventloop::remove_channel(Channel *channel) {
    assert(channel->owner_loop() == this);
    ASSERT_IN_THIS_THREAD();
    if (_eventHandling) {
        assert(_currentActiveChannel == channel || 
               std::find(_activeChannels.begin(), _activeChannels.end(),
                    _currentActiveChannel) == _activeChannels.end());
    }

    _poller->remove_channel(channel);
}